import yoctoQueue from './yocto-queue'

/**
 * @description pLimit方法用于限制并发请求的并发数 参考：{@link https://juejin.cn/post/7127831645167550471}| {@link https://juejin.cn/post/7375083502411333644} | {@link https://juejin.cn/post/7090072797397352462}
 * @param {number} concurrency
 *
 */
const pLimit = function (concurrency) {
  if (
    !Number.isInteger(concurrency) ||
    concurrency === Number.POSITIVE_INFINITY ||
    concurrency < 1
  ) {
    throw new TypeError('Expected `concurrency` to be a number from 1 and up')
  }
  // 初始化队列
  const queue = new yoctoQueue()
  // 初始化计数器
  let activeCount = 0

  /**
   * @description 方法用于执行下一个等待的请求（可以执行下一个请求的原因是因为当前执行的一些请求中，某个执行完毕了，所以需要将activeCount减少1。然后检查队列中如果有元素则出队。）
   */
  const next = () => {
    activeCount--
    if (queue.size > 0) {
      queue.dequeue()()
    }
  }

  /**
   *@description run方法用来执行异步请求任务
   */
  const run = async (fn, resolve, args) => {
    activeCount++
    const result = (async () => fn(...args))() // fn执行后返回promise实例， result也是一个promise实例，其返回的结果是fn的返回值
    resolve(result)
    try {
      // 为了保证next的顺序需要 await result
      await result
    } catch (error) {}
    next()
  }

  /**
   * @description enqueue方法用于将任务入队执行 (共享一个队列)
   */
  const enqueue = async (fn, resolve, args) => {
    queue.enqueue(run.bind(undefined, fn, resolve, args))
    ;(async () => {
      await Promise.resolve()
      if (activeCount < concurrency && queue.size > 0) {
        queue.dequeue()() // 这个是异步执行run方法的，所以activeCount是异步更新的.if语句中的比较也需要异步进行，需要等到下一个微任务之后，以获取`activeCount的最新值
      }
    })()
  }

  /**
   * @description 返回一个promise，promise中调用入队enqueue方法。
   */
  const generator = (fn, ...args) => {
    return new Promise((resolve) => {
      enqueue(fn, resolve, args)
    })
  }

  Object.defineProperties(generator, {
    // 一些额外属性的定义
    activeCount: {
      get: () => activeCount
    },
    pendingCount: {
      get: () => queue.size
    },
    clearQueue: {
      value: () => {
        queue.clear()
      }
    }
  })

  return generator
}

export default pLimit
